#ifndef IGSLIB_IDISPENSER_H
#define IGSLIB_IDISPENSER_H
#include "igslib_common_use.h"
#include "igslib_igpio.h"
#include "igslib_iserial_manager.h"

namespace igslib
{
    struct IDispenserEvent;
	struct IDispenser:public IComponent
	{			
		enum class State
        {
            idle,
            prize_enter,
			prize_busy,
            prize_out,
            error,
            error_recovery,
        };
        enum class ErrorState
        {
            none,
            jam,
            empty,
            invalid_value,
            others,
        };
        /*!
         * 取得目前的錯誤狀態。
         * @return 目前的錯誤狀態。
         */
        virtual ErrorState GetErrorState() = 0;
        /*!
         * 設定出獎機相關事件處理者。
         * @param a0 相關事件處理者。
         */
        virtual void SetEventHandler(IDispenserEvent*)=0;
        /*!
         * 增加出獎機的值。
         * @param a0 要增加的值。
         */
		virtual void AddValue(const float&)=0;
        /*!
         * 設定出獎機的值。
         * @param a0 要設定的值。
         */
		virtual void SetValue(const float&)=0;
        /*!
         * 取得出獎機的值。
         */
		virtual float GetValue()=0;
        /*!
         * 更新出獎機狀態。
         */
		virtual void Update()=0;
        /*!
         * 啟動出獎機。
         */
		virtual void Run()=0;
		/*!
         * 重啟出獎機。
		 * @note 這會初始化出獎機及其流程。
         */
		virtual void Reset()=0;
        /*!
         * 停止出獎機。
         */
		virtual void Stop()=0;
        /*!
         * 停止出獎流程。
         * @note 這會停止出獎機及其流程。
         */
		virtual void AbortDispension()=0;
        /*!
         * 開始出獎。
         * @note 這會啟動出獎機，重置錯誤狀態，重新開始一個出獎流程。
         */
		virtual void PrizeOut()=0;
        /*!
         * 設定多少時間內才會確認為正常出獎，超過此區間範圍的都不會被認定為是正常出獎。
         * @param a0 最短時間。
         * @param a1 最長時間。
		 * @note 時間單位 ms。
         */
		virtual void SetConfirmRange(const float&,const float&)=0;
        /*!
         * 設定超過多少時間會被認定是獎品卡住狀態。
         * @param a0 卡住時間。
         *@note 時間單位 ms。
         */
		virtual void SetJammedTime(const float&)=0;
        /*!
         * 設定超過多少時間沒有訊號會被認定是獎品用完狀態。
         * @param a0 無訊號時間。時間單位依實作決定。
         */
		virtual void SetEmptyTime(const float&)=0;
        /*!
         * 設定錯誤檢查是否啟用。
         * @param a0 false會重置錯誤檢查結果，並關閉錯誤檢查功能，預設為啟用。
         */
		virtual void SetErrorCheck(const bool&)=0;
        //! 傳回錯誤檢查是否啟用。
		virtual bool GetErrorCheck()=0;
        /*!
         * 設定是否以high準位認定為出獎訊號。
         * @param a0 [false]表示為low準位認定為出獎訊號。
         */
		virtual void SetHighActive(const bool&)=0;
        /*!
         * 取得目前運行狀態。
         * @return 目前運行狀態。
         */
        virtual State GetState()=0;
        /*!
         * 若實作是使用SPI通訊介面，用此函式輸入SPI的實作。
         * @param a0 SPI的實作。
         */
        virtual void SetSPI(ISpi*)=0;
        /*!
         * 若實作是使用GPIO通訊介面，用此函式輸入GPIO的實作。
		 * @param a0 GPIO的實作 --Hopper & Ticket Sensor 接腳
         * @param a1 GPIO的實作 --Hopper 馬達正轉接腳
		 * @param a2 GPIO的實作 --Hopper 馬達反轉接腳 ; Ticket時為NULL
         */
		virtual void SetGPIO(IGPIO* ,IGPIO* ,IGPIO* )=0; 
		
	};

    struct IDispenserEvent
    {
        /*!
         * 當出獎機內存值有異動時觸發。
         * @param a0 差量值。
         */
        virtual void OnChangeValue(float)=0;
        /*!
         * 當出獎機確認出獎時觸發。
         * @param a0 出獎值。
         */
        virtual void OnPrizeOut(float)=0;
        /*!
         * 當出獎機啟動時觸發。
         */
        virtual void OnRun()=0;
        /*!
         * 當出獎機停止時觸發。
         */
        virtual void OnStop()=0;
        /*!
         * 當出獎流程發生任何錯誤時時觸發。
         */
        virtual void OnErrorRaised()=0;
        /*!
         * 當狀態改變時觸發。
         * @param a0 改變前狀態。
         * @param a1 改變後狀態。
         */
        virtual void OnChangeState(const IDispenser::State&,const IDispenser::State&)=0;
    };

    //! 給退幣機使用的事件介面。
    struct IHopperEvent:virtual public IDispenserEvent
    {
        /*!
         * 當退幣機進入自定義運轉模式時觸發。
         */
        virtual void OnCustomRun()=0;
    };

    //! 退幣機是一個退獎機，還加上自己專屬的介面。
    struct IHopper:virtual public IDispenser
    {
        //! 自定義運轉模式。
        enum class RunMode
        {
            none,
            clockwise, //!< 順時針方向
            counter_clockwise, //!< 逆時針方向
            force_one_shot, //!< 只能有一幣退出，實作上常以施以反向力以更快的速度停止馬達。
        };
        /*!
         * 取得目前是以何種自定義運轉模式運行。
         */
        virtual RunMode GetCustomRunMode()=0;
        /*!
         * 退幣機在卡幣時，可自行控制轉盤，達到復原效果。
         */
        virtual void JamRecovery()=0;
        /*!
         * 以設定自定義運轉模式啟動退幣機。
         * @param a0 運轉模式。
         */
        virtual void CustomRun(RunMode)=0;
        /*!
         * 設定超過多少時間沒有訊號會被認定是獎品用完狀態。
         * @param a0 正轉無訊號時間。
		 * @param a1 反轉無訊號時間,此參數可為 '0'表不反轉
		 * @param a2 中間停止時間,	此參數可為 '0'表不停止
         * @note 1.時間單位 ms。
		 * 		 2.正轉 a0 ms --> 停 a2 ms --> 反轉 a1 ms --> 停 a2 ms  --> 正轉 a0 ms --> 停 a2 ms--> 
		 *			反轉 a1 ms -->停 a2 ms-->正轉 a1 ms --> 空幣	
         */
		virtual void SetEmptyTime(const float&,const float&,const float&)=0;
    };

    struct IDispenserManager:public IManager<IDispenser*>
    {
		 /*!
         * 創建MH主板上根據專案特規實作MCU資源。
         * @param a0 專案名。
         * @return 根據專案特規實作MCU資源。
         * @note
         *     因應韌體更新功能，使用者需呼叫SetUART()導入通訊介面的實作。
         *     因應保護單元認證功能，使用者需呼叫SetSPI()導入通訊介面的實作。
         *
         *     目前已實作專案名對照如下：
         *     "DragonBallRace"：龍珠賽球
         */
        virtual IHopper* New_ProjMCU(const std::string&)=0;
    };
};

extern "C"{
    igslib::IDispenserManager* GetDispenserManagerInstance();
};

#endif

